package jxtend.processor.eclipse.visitor.replace;

import java.lang.reflect.Field;

import jxtend.processor.utils.ReflectUtils;

import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Statement;

@SuppressWarnings("restriction")
public class ExpressionReplacer implements ASTNodeReplaceHandler {

	@Override public boolean replaceNode(ASTNode parentNode, ASTNode originalNode, ASTNode newNode) {
		if ((!(originalNode instanceof Expression) || !(newNode instanceof Expression))) {
			return false;
		}

		Field[] publicFields = parentNode.getClass().getFields();

		boolean replaced = false;
		
		for (Field publicField : publicFields) {
			//TODO: make more generic; every type that exists from Statement should be supported
			if (publicField.getType().equals(Expression.class) || publicField.getType().equals(Statement.class)) {
				Statement fieldValue = (Statement) ReflectUtils.fieldValue(publicField, parentNode);
				if (fieldValue == originalNode) {
					if (replaced) {
						throw new IllegalStateException("More then one expression exists with the same identity as the "
								+ " to be replaced expression");
					}

					ReflectUtils.setFieldValue(publicField, parentNode, newNode);
					replaced = true;
				}

				//TODO: make more generic; every type that exists from Statement should be supported
			} else if(publicField.getType().equals(Expression[].class)
					|| publicField.getType().equals(Statement[].class)) {
				Statement[] fieldValue = (Statement[]) ReflectUtils.fieldValue(publicField, parentNode);
				if (fieldValue == null) { continue; }

				for (int i = 0; i < fieldValue.length; i++) {
					if (fieldValue[i] == originalNode) {
						if (replaced) {
							throw new IllegalStateException("More then one expression exists with the same identity as "
									+ "the to be replaced expression");
						}

						fieldValue[i] = (Expression) newNode;
						replaced = true;
					}					
				}
			}
		}

		return replaced;
	}
}
